package seminarski.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;

import java.util.ArrayList;
import java.util.Queue;

import javax.swing.JPanel;

import seminarski.geometrija.ConvexHull;
import seminarski.geometrija.Traka;
import seminarski.geometrija.Trio;
import seminarski.gui.Toolbar.Tip;

import common.point.PointG;
import common.segment.SegmentG;

public class MyCanvas extends JPanel implements MouseListener,
		MouseMotionListener {
	private static final long serialVersionUID = 1L;
	private ArrayList<PointG<Double>> points;
	private ConvexHull convexHull;
	private int pointCount = 20;
	Traka traka;
	private SegmentG<Double> segment;
	private SegmentG<Double> segment1;
	private PointG<Double> point;
	private int brojac;
	boolean iscrtavaSeTocak;
	private int speed = 500;
	private Dimension lastSize = new Dimension(0, 0);
	private BufferedImage backBuffer = null;
	private boolean iscrtavasePoligon;

	public MyCanvas(int i, int j) {
		initVariables();
		this.setSize(i, j);
		this.setPreferredSize(new Dimension(i, j));
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		this.setBackground(Color.WHITE);
		this.setVisible(true);
	}

	private void initVariables() {
		points = new ArrayList<PointG<Double>>();
		// App.getApp().getStatusBar().setStatus(brojac + "" );
		convexHull = new ConvexHull();
	}

	public ArrayList<PointG<Double>> getPoints() {
		return points;
	}

	public ConvexHull getConvexHull() {
		return convexHull;
	}

	public void crtajPoligon(int size) {
		brojac = 0;
		setIscrtavasePoligon(true);
		for (int i = 0; i < size; i++) {
			brojac++;
			try {
				Thread.sleep(speed);

			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			repaint();
		}
	}

	@Override
	public void paint(Graphics gr) {
		if (backBuffer == null || !lastSize.equals(this.getSize())) {
			backBuffer = new BufferedImage(this.getWidth(), this.getHeight(),
					BufferedImage.TYPE_3BYTE_BGR);
			lastSize = this.getSize();
		}
		Graphics g = backBuffer.getGraphics();

		RenderingHints renderHints = new RenderingHints(
				RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		renderHints.put(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);
		Graphics2D g2d = (Graphics2D) g;
		g2d.setBackground(Color.white);
		g2d.clearRect(0, 0, this.getWidth(), this.getHeight());
		g2d.setRenderingHints(renderHints);
		g2d.setColor(Color.BLUE);
		
		for (int k = 0; k < points.size(); k++) {
			PointG<Double> p = points.get(k);
			g2d.fillOval(p.x().intValue(), p.y().intValue(), 5, 5);
		}
//		if(points.size() > 0)
//			convexHull.convexHull(points);
		ArrayList<PointG<Double>> vertices = convexHull.getHull();
		g2d.setColor(Color.BLUE.brighter());
		if ((convexHull.hullSize()) > 0) {
			int i;
			if (!isIscrtavasePoligon())
				brojac = convexHull.hullSize() - 1;

			for (i = 0; i < brojac; i++) {

				g2d
						.drawLine(vertices.get(i).x().intValue() + 2, vertices
								.get(i).y().intValue() + 2, vertices.get(i + 1)
								.x().intValue() + 2, vertices.get(i + 1).y()
								.intValue() + 2);
			}

			if (brojac == convexHull.hullSize() - 1) {
				g2d.drawLine(vertices.get(i).x().intValue() + 2, vertices.get(i)
						.y().intValue() + 2, vertices.get(0).x().intValue() + 2,
						vertices.get(0).y().intValue() + 2);
			}
			if (iscrtavaSeTocak) {
				g2d.setColor(Color.BLACK);
				if (segment != null) {
					g2d.drawLine(segment.p1().x().intValue() + 2, segment.p1()
							.y().intValue() + 2,
							segment.p2().x().intValue() + 2, segment.p2().y()
									.intValue() + 2);
					g2d.drawLine(segment1.p1().x().intValue() + 2, segment1
							.p1().y().intValue() + 2, segment1.p2().x()
							.intValue() + 2, segment1.p2().y().intValue() + 2);
					g2d.fillOval(point.x().intValue(), point.y().intValue(), 5,
							5);
				}
			}

		}

		gr.drawImage(backBuffer, 0, 0, null);
	}

	@Override
	public void mouseClicked(MouseEvent e) {
	}

	@Override
	public void mouseEntered(MouseEvent e) {
	}

	@Override
	public void mouseExited(MouseEvent e) {
	}

	@Override
	public void mousePressed(MouseEvent e) {
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		Toolbar toolbar = App.getApp().getToolbar();
		if (toolbar.getTip() != Tip.MANUAL) {
			toolbar.setTip(Tip.MANUAL);
			toolbar.setManual(true);
		}
		this.points
				.add(new PointG<Double>((double) e.getX(), (double) e.getY()));
		repaint();
	}

	public void iscrtajTocak() {
		iscrtavaSeTocak = true;
		traka = new Traka(convexHull);
		traka.findUgaonaTraka();
		Queue<Trio<PointG<Double>, SegmentG<Double>>> q1 = traka.getQueue();
		Trio<PointG<Double>, SegmentG<Double>> p = q1.poll();
		segment = p.getSegment1();
		segment1 = p.getSegment2();
		point = p.getTacka();
		try {
			Thread.sleep(2 * speed);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		repaint();
		while (!q1.isEmpty()) {
			p = q1.poll();
			segment = p.getSegment1();
			segment1 = p.getSegment2();
			point = p.getTacka();
			try {
				Thread.sleep(speed);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			repaint();
		}
	}

	public void setPointCount(int newValue) {
		this.pointCount = newValue;
	}

	public int getPointCount() {
		return this.pointCount;
	}

	@Override
	public void mouseDragged(MouseEvent e) {
	}

	@Override
	public void mouseMoved(MouseEvent e) {
		// App.getApp().getStatusBar().setPosition("(" + e.getPoint().x + ", " +
		// e.getPoint().y + ")");

	}

	public void resetTocak() {
		segment = null;
		segment1 = null;
		point = null;

	}

	public void setSpeed(int speed) {
		this.speed = speed;
	}

	public int getSpeed() {
		return speed;
	}

	/**
	 * @param iscrtavasePoligon
	 *            the iscrtavasePoligon to set
	 */
	public void setIscrtavasePoligon(boolean iscrtavasePoligon) {
		this.iscrtavasePoligon = iscrtavasePoligon;
	}

	/**
	 * @return the iscrtavasePoligon
	 */
	public boolean isIscrtavasePoligon() {
		return iscrtavasePoligon;
	}

	public void clearSegments() {
		segment = null;
		segment1 = null;
	}

	public void setPoints(ArrayList<PointG<Double>> points) {
		this.points = points;		
	}

}
